package edu.hzau.xiahui.api;

import com.netease.lowcode.core.annotation.NaslLogic;
import edu.hzau.xiahui.vo.CalculateDetail;
import edu.hzau.xiahui.vo.CalculateParam;
import edu.hzau.xiahui.vo.InsuranceThreshold;
import edu.hzau.xiahui.vo.TaxStep;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static edu.hzau.xiahui.constants.Constants.*;

@Component
public class CalculateDetailApi {
    @NaslLogic
    public List<CalculateDetail> details(CalculateParam param) {
        List<CalculateDetail> res = new ArrayList<>(12);
        BigDecimal lastAccTaxBase = BigDecimal.ZERO;
        BigDecimal lastAccTax = BigDecimal.ZERO;
        final BigDecimal DEDUCT = deduct(param);
        final BigDecimal INSURANCE = insurance(param);

        for (int i = 0; i < YEAR; i++) {
            BigDecimal incomeBeforeTax = param.salary;
            if (i + 1 == param.bonusMonth) {
                incomeBeforeTax = incomeBeforeTax.add(param.bonus);
            }

            final BigDecimal accTaxBase = lastAccTaxBase.add(incomeBeforeTax).subtract(MONTH_TAX_FREE).subtract(INSURANCE).subtract(Optional.ofNullable(DEDUCT).orElse(BigDecimal.ZERO));
            TaxStep taxStep = YEAR_TAX_STEPS.stream().filter(x -> accTaxBase.compareTo(x.threshold) <= 0).findFirst().orElseThrow(IllegalArgumentException::new);
            BigDecimal accTax = accTaxBase.multiply(taxStep.taxRate).subtract(taxStep.deduct);

            BigDecimal tax = accTax.subtract(lastAccTax);
            res.add(new CalculateDetail(
                    incomeBeforeTax.subtract(INSURANCE).subtract(tax),
                    incomeBeforeTax,
                    DEDUCT,
                    INSURANCE,
                    tax
            ));

            lastAccTaxBase = accTaxBase;
            lastAccTax = accTax;
        }

        return res;
    }

    private BigDecimal deduct(CalculateParam param) {
        BigDecimal deduct = BigDecimal.ZERO;
        deduct = deduct.add(param.childEdu != null ? param.childEdu : BigDecimal.ZERO);
        deduct = deduct.add(DEGREE_EDU.equals(param.continueEdu) ? param.continueEdu : BigDecimal.ZERO);
        deduct = deduct.add(param.houseLoan != null ? param.houseLoan : BigDecimal.ZERO);
        deduct = deduct.add(param.houseRent != null ? param.houseRent : BigDecimal.ZERO);
        deduct = deduct.add(param.parentSupport != null ? param.parentSupport : BigDecimal.ZERO);
        deduct = deduct.add(param.childSupport != null ? param.childSupport : BigDecimal.ZERO);
        return deduct.equals(BigDecimal.ZERO) ? null : deduct;
    }

    private BigDecimal insurance(CalculateParam param) {
        BigDecimal insurance = BigDecimal.ZERO;

        BigDecimal incomeBeforeTax = param.salary.multiply(new BigDecimal(YEAR)).add(param.bonus);
        InsuranceThreshold insuranceThreshold = INSURANCE_THRESHOLDS.get("wuhan");
        BigDecimal insuranceBase = incomeBeforeTax.compareTo(insuranceThreshold.insurance[0].multiply(new BigDecimal(YEAR))) < 0
                ? insuranceThreshold.insurance[0]
                : incomeBeforeTax.compareTo(insuranceThreshold.insurance[1].multiply(new BigDecimal(YEAR))) > 0
                ? insuranceThreshold.insurance[1]
                : incomeBeforeTax.divide(new BigDecimal(YEAR), 2, RoundingMode.HALF_UP);
        insurance = insurance.add(insuranceBase.multiply(param.pensionInsurance.add(param.healthInsurance).add(param.unemploymentInsurance)));

        BigDecimal houseFundBase = incomeBeforeTax.compareTo(insuranceThreshold.houseFund[0].multiply(new BigDecimal(YEAR))) < 0
                ? insuranceThreshold.houseFund[0]
                : incomeBeforeTax.compareTo(insuranceThreshold.houseFund[1].multiply(new BigDecimal(YEAR))) > 0
                ? insuranceThreshold.houseFund[1]
                : incomeBeforeTax.divide(new BigDecimal(YEAR), 2, RoundingMode.HALF_UP);
        insurance = insurance.add(houseFundBase.multiply(param.houseFund));

        insurance = insurance.add(param.enterpriseAnnuity != null ? param.enterpriseAnnuity : BigDecimal.ZERO);
        return insurance;
    }
}
